package com.own.component.common.util.list;

import com.alibaba.fastjson2.JSON;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * ListSortUtil
 *
 * @author chenxueli
 * @target 支持多层级的排序，同层级数据自定义排序
 * @date 2023-03-21 16:40:00
 */
@Slf4j
public class ListSortModel<T> {

    /**
     * 关键字比较字段
     */
    private final List<KeySort<T, ?>> keySortList;

    /**
     * 普通比较字段
     */
    private final List<Comparator<? super T>> comparatorList;

    /**
     * 类型列表
     */
    private final List<String> typeList;

    public ListSortModel() {
        this.keySortList = new ArrayList<>();
        this.comparatorList = new ArrayList<>();
        this.typeList = new ArrayList<>();
    }

    public static void main(String[] args) {
        var list = new ArrayList<TestUser>();
        list.add(new TestUser("张三", 18));
        list.add(new TestUser("李四", 20));
        list.add(new TestUser("王五", 19));
        list.add(new TestUser("赵六", 18));
        list.add(new TestUser("田七", 20));
        list.add(new TestUser("孙八", 18));
        // 执行排序
        var resultList = new ListSortModel<TestUser>()
                .add(Comparator.comparing(TestUser::getAge))
                .add(TestUser::getName, List.of("李四", "赵六", "王五", "田七", "孙八", "张三"))
                .sort(list);
        System.out.println("JSON.toJSONString(resultList) = " + JSON.toJSONString(resultList));
    }

    /**
     * 添加关键字比较器
     *
     * @param extractor 比较字段
     * @param keyList   关键字列表
     * @param <R>       关键字类型
     * @return ListSortModel
     */
    public <R> ListSortModel<T> add(Function<T, R> extractor, List<R> keyList) {
        this.keySortList.add(KeySort.<T, R>builder().extractor(extractor).keyList(keyList).build());
        this.typeList.add(Type.KEY);
        return this;
    }

    /**
     * 添加关键字比较器
     *
     * @param comparator 比较器
     * @param <R>        关键字类型
     * @return ListSortModel
     */
    public <R> ListSortModel<T> add(Comparator<? super T> comparator) {
        this.comparatorList.add(comparator);
        this.typeList.add(Type.COMPARATOR);
        return this;
    }

    public List<T> sort(List<T> list) {
        if (list == null || list.isEmpty()) {
            return list;
        }
        return list.stream().sorted((item1, item2) -> {
            var index1 = 0;
            var index2 = 0;
            for (var type : this.typeList) {
                var compare = switch (type) {
                    case Type.KEY -> {
                        var keySort = this.keySortList.get(index1++);
                        var keyList = keySort.keyList;
                        // 获取对应的值
                        var value1 = keySort.extractor.apply(item1);
                        var value2 = keySort.extractor.apply(item2);
                        // 进行有序序列的比较
                        yield Integer.compare(keyList.indexOf(value1), keyList.indexOf(value2));
                    }
                    case Type.COMPARATOR -> {
                        var comparator = this.comparatorList.get(index2++);
                        yield comparator.compare(item1, item2);
                    }
                    default -> 0;
                };
                // 如果相等则继续比较下一个
                if (compare == 0) {
                    continue;
                }
                return compare;
            }
            return 0;
        }).collect(Collectors.toList());
    }

    private interface Type {
        String KEY = "key";
        String COMPARATOR = "comparator";
    }

    @Builder
    private static class KeySort<T, R> {
        /**
         * 排序key
         */
        private List<R> keyList;
        /**
         * 需要排序的字段
         */
        private Function<T, R> extractor;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class TestUser {
        private String name;
        private Integer age;
    }

}
